/*************************************************
 * ThinkJot V2
 * Author: Jeswin P. (jeswin@process64.com)
 * Website: http://www.process64.com/thinkjot/
 * This code is distributed under the terms of the
 * Apache License, Version 2.0.
 * **********************************************/

/* 
 * This class is _complicated_. 
 * REFACTOR! 
 * Maybe not because I hate regexen!! No, not at all.
 */

using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml;
using System.Configuration;
using System.Web;

using ThinkJot.Core.Configuration;
using ThinkJot.Core.Utils;
using ThinkJot.Core.Blogs.Providers;
using ThinkJot.Core.Blogs;

namespace ThinkJot.Core.UrlMapping
{
    public static class UrlMappingUtility
    {
        static List<IUrlMapEntry> urlMap = new List<IUrlMapEntry>();
        static List<string> definedRootUrls = new List<string>();
        static List<string> injectedBlogUrls = new List<string>();

        static UrlMappingUtility()
        {
            BuildSortedUrlMap();
            LoadPredefinedRootUrls();
            LoadInjectedBlogUrls();
        }

        private static void LoadPredefinedRootUrls()
        {
            //Some system urls exist at the root folder. 
            //We should not try to url map them, in case any UrlMapping points at the application root.
            definedRootUrls = new List<string>(new string[] { "login.aspx", "captchaimagegen.aspx" });
        }

        private static void LoadInjectedBlogUrls()
        {
            //Define all system mapped urls here; ie- those that get injected to every blog
            //We will not pass it through; rather redirect to default impl
            injectedBlogUrls = new List<string>(new string[] { "metaweblogapi.aspx", "getrsd.aspx" });
        }


        //We need ultra-fast url mappings. So, not using the regexens. <= Lame Excuse!
        public static string GetMatchingRewrite(string url)
        {
            string appPath = HttpContext.Current.Request.ApplicationPath;
            SystemConfiguration sysConfig = SystemConfiguration.GetConfig();

            foreach (string rootUrl in definedRootUrls)
            {
                if (url.ToLower() == new PathUtility().CombineUrls(appPath, rootUrl).ToLower())
                    return null;
            }

            foreach (IUrlMapEntry mapEntry in urlMap)
            {
                if (mapEntry is BlogUrlMapEntry)
                {
                    BlogUrlMapEntry blogUrlMapEntry = (BlogUrlMapEntry)mapEntry;

                    string strippedUrl = StripUrlOfPath(url, blogUrlMapEntry, appPath);

                    //A set of urls are injected into every blog path.
                    if (injectedBlogUrls.Contains(strippedUrl))
                        return "/blogs/" + strippedUrl;

                    if (strippedUrl != null)
                    {
                        if (strippedUrl.ToLower().StartsWith("default.aspx")
                            || strippedUrl.ToLower().StartsWith("adminpage.aspx"))
                        {
                            return "/blogs/" + blogUrlMapEntry.BlogName + "/" + strippedUrl;
                        }
                        foreach (string ext in blogUrlMapEntry.UrlMapExtensions)
                        {
                            string entryTitle;
                            if (strippedUrl.EndsWith("." + ext))
                            {
                                entryTitle = strippedUrl.Remove(strippedUrl.Length - ("." + ext).Length);
                                Guid entryId = BlogEntry.GetEntryIdForTitle(entryTitle, blogUrlMapEntry.BlogName);

                                if (entryId == Guid.Empty) //no such post, pass it on. (let it throw a 404)
                                    return url; //do nothing

                                //We are having the provision to redirect some pages (say aboutus.aspx) to
                                //  templates other than default.aspx.
                                //  This is neat, since some pages need to look _unlike_ a blog post.
                                string template = TemplateMap.GetTemplateForEntry(entryId, blogUrlMapEntry.BlogName);
                                return "/blogs/" + blogUrlMapEntry.BlogName + "/" + template + "?function=DisplayItem&showComments=true&entryGuid=" + entryId.ToString();
                            }
                        }
                    }
                }
                //Other than BlogUrlMapEntry code goes here.....
                // if (mapEntry is xxx_xxx) { ...... }
            }
            return null;
        }

        private static string StripUrlOfPath(string url, BlogUrlMapEntry mapEntry, string appPath)
        {
            url = url.Substring(appPath.Length).ToLower().Trim('/');
            string blogPath = mapEntry.Url.ToLower().Trim('/'); // => process64
            string fullBlogPath = ("blogs/" + mapEntry.BlogName.ToLower()).Trim('/'); // => blogs/process64

            if (url == blogPath || url == fullBlogPath) return "default.aspx";

            //make blog path look like "/<folder-name>/"
            if (blogPath != "")
                blogPath = "/" + blogPath + "/";
            else
                //or "/" if it is empty
                blogPath = "/";

            //make blog path look like "/blogs/<folder-name>/"
            fullBlogPath = "/" + fullBlogPath + "/";

            url = "/" + url;

            //if the url does not end with an extension, make it look like "/<folder-name1>/<some-other>/"
            //  else let it be "/<folder-name>/somepage.aspx" (unchanged)

            int lastSlash = url.LastIndexOf("/");
            if (lastSlash != -1)
            {
                string lastPart = url.Substring(lastSlash);
                if (!lastPart.Contains("."))
                    url = url + "/";
            }

            string deprefixed;

            //url(/process64/helloworld.aspx) => helloworld.aspx
            if (url.StartsWith(fullBlogPath))
                deprefixed = url.Substring(fullBlogPath.Length);
            else if (url.StartsWith(blogPath))
                deprefixed = url.Substring(blogPath.Length);
            else
                return null;

            //inspires.aspx => YES
            if (deprefixed.Contains("/"))
                return null;
            else
                return deprefixed;
        }

        private static void BuildSortedUrlMap()
        {
            IUrlMapEntry entry;

            //Get Mapping For Blogs
            Dictionary<string, BlogConfig> blogList = SystemConfiguration.GetConfig().Blogs.GetAll();
            foreach (BlogConfig blog in blogList.Values)
            {
                if (!blog.UseUrlMapping) continue;  //Some blogs might prefer the full Url
                string altPath = blog.AlternateUrl ?? "";
                //Append a '/' to the url for easier, safe comparisons
                altPath = altPath.ToLower().Trim('/');
                entry = new BlogUrlMapEntry(blog.Name, altPath, blog.UrlMapExtensions);
                urlMap.Add(entry);
            }

            //Get Mapping For Other Types code goes here.....

            urlMap.Sort(Compare_UrlMapEntries_ByLength);
            urlMap.Reverse();   //We need the longest one first.
        }

        private static int Compare_UrlMapEntries_ByLength(IUrlMapEntry entry1, IUrlMapEntry entry2)
        {
            string x = entry1.Url;
            string y = entry2.Url;
            if (x == null)
            {
                if (y == null)
                {
                    // If x is null and y is null, they're
                    // equal. 
                    return 0;
                }
                else
                {
                    // If x is null and y is not null, y
                    // is greater. 
                    return -1;
                }
            }
            else
            {
                // If x is not null...
                //
                if (y == null)
                // ...and y is null, x is greater.
                {
                    return 1;
                }
                else
                {
                    // ...and y is not null, compare the 
                    // lengths of the two strings.
                    //
                    int retval = x.Length.CompareTo(y.Length);

                    if (retval != 0)
                    {
                        // If the strings are not of equal length,
                        // the longer string is greater.
                        //
                        return retval;
                    }
                    else
                    {
                        // If the strings are of equal length,
                        // sort them with ordinary string comparison.
                        //
                        return x.CompareTo(y);
                    }
                }
            }
        }
    }
}